package bev.article;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;

import javax.swing.JTextPane;
import javax.swing.JLabel;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.TabSet;
import javax.swing.text.TabStop;

public class ArticleFormatter {
    // Text attributes.
    /**
     * The standard text attributes used for <b>small headers</b> when rendering
     * articles.
     */
    private static SimpleAttributeSet sasSmallHeader;
    /**
     * The standard text attributes used for <b>medium headers</b> when
     * rendering articles.
     */
    private static SimpleAttributeSet sasMedHeader;
    /**
     * The standard text attributes used for <b>large headers</b> when rendering
     * articles.
     */
    private static SimpleAttributeSet sasLargeHeader;
    /**
     * The standard text attributes used for <b>regular text</b> when rendering
     * articles.
     */
    private static SimpleAttributeSet sasRegular;

    static {
        defineAttributes();
    }

    /**
     * Defines the standard attributes to use across articles for things like
     * headers or regular text. Any attributes needed in an article should be
     * defined here to keep the format unified across articles.
     */
    private static void defineAttributes() {
        // Small header attributes.
        sasSmallHeader = new SimpleAttributeSet();
        StyleConstants.setForeground( sasSmallHeader, Color.BLACK );
        StyleConstants.setFontFamily( sasSmallHeader, "Arial" );
        StyleConstants.setBold( sasSmallHeader, true );
        StyleConstants.setFontSize( sasSmallHeader, 14 );

        // Medium header attributes.
        sasMedHeader = new SimpleAttributeSet();
        StyleConstants.setForeground( sasMedHeader, Color.BLACK );
        StyleConstants.setFontFamily( sasMedHeader, "Arial" );
        StyleConstants.setBold( sasMedHeader, true );
        StyleConstants.setFontSize( sasMedHeader, 18 );

        // Large header attributes.
        sasLargeHeader = new SimpleAttributeSet();
        StyleConstants.setForeground( sasLargeHeader, Color.BLACK );
        StyleConstants.setFontFamily( sasLargeHeader, "Arial" );
        StyleConstants.setBold( sasLargeHeader, true );
        StyleConstants.setFontSize( sasLargeHeader, 24 );

        // Regular text attributes.
        sasRegular = new SimpleAttributeSet();
        StyleConstants.setForeground( sasRegular, Color.BLACK );
        StyleConstants.setFontFamily( sasRegular, "Arial" );
        StyleConstants.setFontSize( sasRegular, 14 );
    }

    /**
     * Sets the tab size in characters to use for a JTextPane (used to
     * render/display articles).
     * 
     * @param textPane
     *            The text pane on which to change the tab size.
     * @param tabSize
     *            The new size of tabs to use (in characters).
     * @param tabCount
     *            The number of tab stops to support per line (50 is a good
     *            number).
     */
    public static void setTabSize( JTextPane textPane, int tabSize, int tabCount ) {
        // Extract style data from the 'regular' text attribute set. This will
        // be used to create a Font object for font metrics (which will be used
        // to determine the width of characters, used to set the tab width).
        int style = Font.PLAIN;
        if ( StyleConstants.isBold( sasRegular ) )
            style += Font.BOLD;
        if ( StyleConstants.isItalic( sasRegular ) )
            style += Font.ITALIC;

        // Font object reflecting the settings in the regular text
        // SimpleAttributeSet.
        Font font = new Font( StyleConstants.getFontFamily( sasRegular ),
                style, StyleConstants.getFontSize( sasRegular ) );

        // Get the character width for this font ('W' is used).
        // Note: This is a silly way to do this (using a label), but Java
        // deprecated the 'Toolkit' way of doing it, so this is the next best
        // way.
        JLabel label = new JLabel();
        FontMetrics fm = label.getFontMetrics( font );
        int charWidth = fm.charWidth( 'W' );

        // Create the tab stops based on the requested
        // tab width and count of tab stops per line.
        TabStop[] tabStops = new TabStop[tabCount];
        for ( int i = 0; i < tabStops.length; ++i ) {
            tabStops[i] = new TabStop( ( i + 1 ) * charWidth * tabSize );
        }

        // Set the tab stops in the textpane.
        SimpleAttributeSet sas = new SimpleAttributeSet();
        TabSet ts = new TabSet( tabStops );
        StyleConstants.setTabSet( sas, ts );
        textPane.setParagraphAttributes( sas, false );
    }

    /**
     * Adds a <b>small header</b> text line to a text pane. Used to add small
     * headers to articles when rendering their 'view modes'.
     * 
     * @param textPane
     *            The text pane the small header line is being added to.
     * @param header
     *            The header text to add.
     */
    public static void addSmallHeader( JTextPane textPane, String header ) {
        try {
            textPane.getDocument().insertString(
                    textPane.getDocument().getLength(), header, sasSmallHeader );
        } catch ( BadLocationException ex ) {
            ex.printStackTrace();
        }
    }

    /**
     * Adds a <b>medium header</b> text line to a text pane. Used to add medium
     * headers to articles when rendering their 'view modes'.
     * 
     * @param textPane
     *            The text pane the header line is being added to.
     * @param header
     *            The header text to add.
     */
    public static void addMedHeader( JTextPane textPane, String header ) {
        try {
            textPane.getDocument().insertString(
                    textPane.getDocument().getLength(), header, sasMedHeader );
        } catch ( BadLocationException ex ) {
            ex.printStackTrace();
        }
    }

    /**
     * Adds a <b>large header</b> text line to a text pane. Used to add large
     * headers to articles when rendering their 'view modes'.
     * 
     * @param textPane
     *            The text pane the header line is being added to.
     * @param header
     *            The header text to add.
     */
    public static void addLargeHeader( JTextPane textPane, String header ) {
        try {
            textPane.getDocument().insertString(
                    textPane.getDocument().getLength(), header, sasLargeHeader );
        } catch ( BadLocationException ex ) {
            ex.printStackTrace();
        }
    }

    /**
     * Adds a <b>regular</b> text line to a text pane. Used to add normal lines
     * of text to articles when rendering their 'view modes'.
     * 
     * @param textPane
     *            The text pane the article text is being added to.
     * @param text
     *            The text to add.
     */
    public static void addRegular( JTextPane textPane, String text ) {
        try {
            textPane.getDocument().insertString(
                    textPane.getDocument().getLength(), text, sasRegular );
        } catch ( BadLocationException ex ) {
            ex.printStackTrace();
        }
    }

    /**
     * Adds a single newline character to a text pane for text spacing purposes.
     * 
     * @param textPane
     *            The text pane to add a single newline to.
     */
    public static void addNewLine( JTextPane textPane ) {
        try {
            textPane.getDocument().insertString(
                    textPane.getDocument().getLength(), "\n", sasRegular );
        } catch ( BadLocationException ex ) {
            ex.printStackTrace();
        }
    }

    /**
     * Adds multiple newline characters to a text pane for text spacing
     * purposes.
     * 
     * @param textPane
     *            The text pane to add the newlines to.
     * @param count
     *            The number of newlines to add.
     */
    public static void addNewLine( JTextPane textPane, int count ) {
        if ( count < 0 ) {
            System.err
                    .print( "ERROR: Expeted 0 or greater for count of newlines." );
            return;
        } else if ( count == 0 ) {
            return;
        }

        String newLines = "";
        for ( int i = 0; i < count; ++i ) {
            newLines = String.format( "%s\n", newLines );
        }

        try {
            textPane.getDocument().insertString(
                    textPane.getDocument().getLength(), newLines, sasRegular );
        } catch ( BadLocationException ex ) {
            ex.printStackTrace();
        }
    }

    /**
     * Adds a text line to a text pane using a custom attribute set. Used to add
     * non-standard format lines of text to articles when rendering their 'view
     * modes'.
     * 
     * @param textPane
     *            The text pane the custom-attribute article text is being added
     *            to.
     * @param customAttribute
     *            A SimpleAttributeSet object describing the format/appearance
     *            of the text.
     * @param text
     *            The text to add.
     */
    public static void addCustom( JTextPane textPane,
            SimpleAttributeSet customAttribute, String text ) {
        try {
            textPane.getDocument().insertString(
                    textPane.getDocument().getLength(), text, customAttribute );
        } catch ( BadLocationException ex ) {
            ex.printStackTrace();
        }
    }
}
